import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;

public class MyHttpServer {
    // 事件轮询器
    private static Selector selector;
    // 服务通道
    private static ServerSocketChannel serverSocketChannel;

    public static void main(String[] args) throws Exception {
        // 创建 ServerSocketChannel
        serverSocketChannel = ServerSocketChannel.open();
        // 监听 80 端口
        serverSocketChannel.socket().bind(new InetSocketAddress(80));
        // 设置为非阻塞模式
        serverSocketChannel.configureBlocking(false);
        // 为 ssc 注册选择器
        selector = Selector.open();
        /**
         * 将通道注册到选择器上, 并且指定“监听接收事件”
         * SelectionKey.OP_ACCEPT —— 接收连接继续事件，表示服务器监听到了客户连接，服务器可以接收这个连接了
         * SelectionKey.OP_CONNECT —— 连接就绪事件，表示客户与服务器的连接已经建立成功
         * SelectionKey.OP_READ —— 读就绪事件，表示通道中已经有了可读的数据，可以执行读操作了（通道目前有数据，可以进行读操作了）
         * SelectionKey.OP_WRITE —— 写就绪事件，表示已经可以向通道写数据了（通道目前可以用于写操作）
         */
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        // 创建处理器
        while (true) {
            // 等待请求，每次等待阻塞 3s，超过 3s 后线程继续向下运行，如果传入 0 或者不传参数将一直阻塞
            if (selector.select(3000) == 0) {
                continue;
            }
            // 获取待处理的SelectionKey
            Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();

            while (keyIter.hasNext()) {
                SelectionKey key = keyIter.next();
                // 从待处理的 SelectionKey 迭代器中移除当前所使用的 key
                keyIter.remove();
                // 启动新线程处理 SelectionKey
                new Thread(new HttpHandler(key)).run();
            }
        }
    }

    private static class HttpHandler implements Runnable {
        private int bufferSize = 1024;
        private String localCharset = "UTF-8";
        private SelectionKey key;

        public HttpHandler(SelectionKey key) {
            this.key = key;
        }

        public void handleAccept() throws IOException {
            serverSocketChannel = (ServerSocketChannel) key.channel();
            // 建立和客户端的链接， 因为 OP_ACCEPT 是注册在 serverSocketChannel上，每个客户又有自己的 SocketChannel 通道
            SocketChannel clientChannel = serverSocketChannel.accept();
            clientChannel.configureBlocking(false);
            // 开启注册该客户端的可读时间，即该客户上传完所有的请求数据到系统 kernel 的 buffer 缓存中后，开启可读事件通知
            clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
        }

        public void handleRead() throws IOException {
            // 获取 channel
            SocketChannel sc = (SocketChannel) key.channel();
            sc.configureBlocking(false);
            // 获取 buffer 并重置
            ByteBuffer buffer = (ByteBuffer) key.attachment();
            buffer.clear();
            // 没有读到内容则关闭
            if (sc.read(buffer) == -1) {
                sc.close();
            } else {
                // 接收请求数据
                buffer.flip();
                String receivedString = Charset.forName(localCharset).newDecoder().decode(buffer).toString();
                // 控制台打印请求报文头
                String[] requestMessage = receivedString.split("\r\n");
                for (String s : requestMessage) {
                    System.out.println(s);
                    // 遇到空行说明报文头已经打印完
                    if (s.isEmpty()) {
                        break;
                    }
                }
                // 根据上面的处理情况，再注册这里的信息
                sc.register(key.selector(), SelectionKey.OP_WRITE, requestMessage);
            }
        }

        public void handleWrite() throws IOException {
            SocketChannel sc = (SocketChannel) key.channel();
            sc.configureBlocking(false);
            String[] requestMessage = (String[]) key.attachment();

            // 返回客户端
            StringBuilder sendString = new StringBuilder();
            sendString.append("HTTP/1.1 200 OK\r\n"); // 响应报文首行，200 表示处理成功
            sendString.append("Content-Type:text/html;charset=" + localCharset + "\r\n");
            sendString.append("\r\n"); // 报文头结束后加一个空行
            sendString.append("<html><head><title>显示报文</title></head><body>");
            sendString.append("接收到请求报文是：<br/>");
            for (String s : requestMessage) {
                sendString.append(s + "<br/>");
            }
            sendString.append("</body></html>");
            ByteBuffer buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharset));
            sc.write(buffer);
            sc.close();

            // 根据上面的处理情况，再注册这里的信息
            // sc.register(key.selector(), SelectionKey.OP_CONNECT);
        }

        @Override
        public void run() {
            try {
                if (key.isAcceptable()) {
                    // 接收到连接请求时
                    handleAccept();
                } else if (key.isReadable()) {
                    // 读数据
                    handleRead();
                } else if (key.isWritable()) {
                    // 写数据
                    handleWrite();
                } else if (key.isConnectable()) {
                    System.out.println("isConnectable============");
                } else if (key.isValid()) {
                    System.out.println("isValid==================");
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}